package com.cow.framework.utils.file.excel;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.commons.beanutils.converters.DateConverter;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import com.cow.framework.core.config.constant.MessageConstants;
import com.cow.framework.core.exception.BusinessException;
import com.cow.framework.core.log.Custom;

/**
 * 该类实现了将一组对象转换为Excel表格，并且可以从Excel表格中读取到一组List对象中 该类利用了BeanUtils框架中的反射完成
 * 使用该类的前提，在相应的实体对象上通过ExcelReources来完成相应的注解
 */
public class ExcelUtil {
	private static ExcelUtil eu = new ExcelUtil();

	private ExcelUtil() {
	}

	public static ExcelUtil getInstance() {
		return eu;
	}

	/**
	 * 处理对象转换为Excel
	 * 
	 * @param template
	 * @param objs
	 * @param clz
	 * @param isClasspath
	 * @return
	 */
	private ExcelTemplate handlerObj2Excel(String template, List objs, Class clz, boolean isClasspath) {
		ExcelTemplate et = ExcelTemplate.getInstance();
		try {
			if (isClasspath) {
				et.readTemplateByClasspath(template);
			} else {
				et.readTemplateByPath(template);
			}
			List<ExcelHeader> headers = getHeaderList(clz);
			Collections.sort(headers);
			// 输出标题
			et.createNewRow();
			for (ExcelHeader eh : headers) {
				et.createCell(eh.getTitle());
			}
			// 输出值
			for (Object obj : objs) {
				et.createNewRow();
				for (ExcelHeader eh : headers) {

					et.createCell(BeanUtils.getProperty(obj, getMethodName(eh)));
				}
			}
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return et;
	}

	/**
	 * 根据标题获取相应的方法名称
	 * 
	 * @param eh
	 * @return
	 */
	private String getMethodName(ExcelHeader eh) {
		String mn = eh.getMethodName().substring(3);
		mn = mn.substring(0, 1).toLowerCase() + mn.substring(1);
		return mn;
	}

	/**
	 * 将对象转换为Excel并且导出，该方法是基于模板的导出，导出到流
	 * 
	 * @param datas
	 *            模板中的替换的常量数据
	 * @param template
	 *            模板路径
	 * @param os
	 *            输出流
	 * @param objs
	 *            对象列表
	 * @param clz
	 *            对象的类型
	 * @param isClasspath
	 *            模板是否在classPath路径下
	 */
	public void exportObj2ExcelByTemplate(Map<String, String> datas, String template, OutputStream os, List objs,
			Class clz, boolean isClasspath) {
		try {
			ExcelTemplate et = handlerObj2Excel(template, objs, clz, isClasspath);
			et.replaceFinalData(datas);
			et.wirteToStream(os);
			os.flush();
			os.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 将对象转换为Excel并且导出，该方法是基于模板的导出，导出到一个具体的路径中
	 * 
	 * @param datas
	 *            模板中的替换的常量数据
	 * @param template
	 *            模板路径
	 * @param outPath
	 *            输出路径
	 * @param objs
	 *            对象列表
	 * @param clz
	 *            对象的类型
	 * @param isClasspath
	 *            模板是否在classPath路径下
	 */
	public void exportObj2ExcelByTemplate(Map<String, String> datas, String template, String outPath, List objs,
			Class clz, boolean isClasspath) {
		ExcelTemplate et = handlerObj2Excel(template, objs, clz, isClasspath);
		et.replaceFinalData(datas);
		et.writeToFile(outPath);
	}

	/**
	 * 将对象转换为Excel并且导出，该方法是基于模板的导出，导出到流,基于Properties作为常量数据
	 * 
	 * @param prop
	 *            基于Properties的常量数据模型
	 * @param template
	 *            模板路径
	 * @param os
	 *            输出流
	 * @param objs
	 *            对象列表
	 * @param clz
	 *            对象的类型
	 * @param isClasspath
	 *            模板是否在classPath路径下
	 */
	public void exportObj2ExcelByTemplate(Properties prop, String template, OutputStream os, List objs, Class clz,
			boolean isClasspath) {
		ExcelTemplate et = handlerObj2Excel(template, objs, clz, isClasspath);
		et.replaceFinalData(prop);
		et.wirteToStream(os);
	}

	/**
	 * 将对象转换为Excel并且导出，该方法是基于模板的导出，导出到一个具体的路径中,基于Properties作为常量数据
	 * 
	 * @param prop
	 *            基于Properties的常量数据模型
	 * @param template
	 *            模板路径
	 * @param outPath
	 *            输出路径
	 * @param objs
	 *            对象列表
	 * @param clz
	 *            对象的类型
	 * @param isClasspath
	 *            模板是否在classPath路径下
	 */
	public void exportObj2ExcelByTemplate(Properties prop, String template, String outPath, List objs, Class clz,
			boolean isClasspath) {
		ExcelTemplate et = handlerObj2Excel(template, objs, clz, isClasspath);
		et.replaceFinalData(prop);
		et.writeToFile(outPath);
	}

	private Workbook handleObj2Excel(List objs, Class clz) {
		Workbook wb = new HSSFWorkbook();
		try {
			// 导出的字段的get方法   
            List<Object[]> methodList = getGetMethod(clz); 
            
			Sheet sheet = wb.createSheet();
			Row r = sheet.createRow(0);
			CellStyle titleStyle = wb.createCellStyle(); 
			titleStyle.setFillForegroundColor((short) 13);// 设置背景色   
			titleStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
			
//			BeanUtilsBean.setInstance(new BeanUtilsBean2());
//			DateConverter converter = new DateConverter();
//		    converter.setPattern("yyyy/MM/dd");
//		    ConvertUtils.register(converter, Date.class);
//		    
		    
		    ConvertUtilsBean convertUtils = new ConvertUtilsBean();

		    DateConverter dateConverter = new DateConverter();
		    dateConverter.setPattern("yyyy-MM-dd");
		    convertUtils.register(dateConverter, java.util.Date.class);
		    convertUtils.register(dateConverter, java.sql.Date.class);
		    convertUtils.register(dateConverter,Timestamp.class);


		//因为要注册converter,所以不能再使用BeanUtils的静态方法了，必须创建BeanUtilsBean实例

		BeanUtilsBean beanUtils = new BeanUtilsBean(convertUtils,new PropertyUtilsBean());

		    
			
			List<ExcelHeader> headers = getHeaderList(clz);
			Collections.sort(headers);
			// 写标题
			for (int i = 0; i < headers.size(); i++) {
				Cell cell = r.createCell(i);
				cell.setCellStyle(titleStyle);
				cell.setCellValue(headers.get(i).getTitle());
			}
			// 写数据
			Object obj = null;
			for (int i = 0; i < objs.size(); i++) {
				r = sheet.createRow(i + 1);
				obj = objs.get(i);
//				for (int j = 0; j < headers.size(); j++) {
//					r.createCell(j).setCellValue(beanUtils.getProperty(obj, getMethodName(headers.get(j))));
//				}
				
				for (int j = 0; j < methodList.size(); j++) {   
                	Method getMethod=(Method)methodList.get(j)[2];  
                    Object value = getMethod.invoke(obj, new Object[] {});   
                    String textValue = getStringValue(value); 
                    r.createCell(j).setCellValue(textValue);           		
                }     
				
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return wb;
	}

	/**
	 * 导出对象到Excel，不是基于模板的，直接新建一个Excel完成导出，基于路径的导出
	 * 
	 * @param outPath
	 *            导出路径
	 * @param objs
	 *            对象列表
	 * @param clz
	 *            对象类型
	 */
	public void exportObj2Excel(String outPath, List objs, Class clz) {
		Workbook wb = handleObj2Excel(objs, clz);
		FileOutputStream fos = null;
		try {
			// 判断目标文件是否存在  
	        File destFile = new File(outPath);   
	        if (destFile.exists()) {  
	            // 删除已经存在的目标文件，无论目标文件是目录还是单个文件  
	            new File(outPath).delete();   
	        } else {  
	            // 如果目标文件所在目录不存在，则创建目录  
	            if (!destFile.getParentFile().exists()) {  
	                destFile.getParentFile().mkdirs();
	            }  
	        }  
			fos = new FileOutputStream(outPath);
			wb.write(fos);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fos != null)
					fos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 导出对象到Excel，不是基于模板的，直接新建一个Excel完成导出，基于流
	 * 
	 * @param os
	 *            输出流
	 * @param objs
	 *            对象列表
	 * @param clz
	 *            对象类型
	 */
	public void exportObj2Excel(OutputStream os, List objs, Class clz) {
		try {
			Workbook wb = handleObj2Excel(objs, clz);
			wb.write(os);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 从类路径读取相应的Excel文件到对象列表
	 * 
	 * @param path
	 *            类路径下的path
	 * @param clz
	 *            对象类型
	 * @param readLine
	 *            开始行，注意是标题所在行
	 * @param tailLine
	 *            底部有多少行，在读入对象时，会减去这些行
	 * @return
	 */
	public List<Object> readExcel2ObjsByClasspath(String path, Class clz, int readLine, int tailLine) {
		Workbook wb = null;
		try {
			wb = new HSSFWorkbook(TemplateFileUtil.getTemplates(path));
			return handlerExcel2Objs(wb, clz, readLine, tailLine);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 从文件路径读取相应的Excel文件到对象列表
	 * 
	 * @param path
	 *            文件路径下的path
	 * @param clz
	 *            对象类型
	 * @param readLine
	 *            开始行，注意是标题所在行
	 * @param tailLine
	 *            底部有多少行，在读入对象时，会减去这些行
	 * @return
	 */
	public List<Object> readExcel2ObjsByPath(String path, Class clz, int readLine, int tailLine) {
		Workbook wb = null;
		try {
			wb = new HSSFWorkbook(TemplateFileUtil.getTemplates(path));
			return handlerExcel2Objs(wb, clz, readLine, tailLine);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 从类路径读取相应的Excel文件到对象列表，标题行为0，没有尾行
	 * 
	 * @param path
	 *            路径
	 * @param clz
	 *            类型
	 * @return 对象列表
	 */
	public List<Object> readExcel2ObjsByClasspath(String path, Class clz) {
		return this.readExcel2ObjsByClasspath(path, clz, 0, 0);
	}

	/**
	 * 从文件路径读取相应的Excel文件到对象列表，标题行为0，没有尾行
	 * 
	 * @param path
	 *            路径
	 * @param clz
	 *            类型
	 * @return 对象列表
	 */
	public List<Object> readExcel2ObjsByPath(String path, Class clz) {
		return this.readExcel2ObjsByPath(path, clz, 0, 0);
	}

	private String getCellValue(Cell c) {
		String o = null;
		switch (c.getCellType()) {
		case Cell.CELL_TYPE_BLANK:
			o = "";
			break;
		case Cell.CELL_TYPE_BOOLEAN:
			o = String.valueOf(c.getBooleanCellValue());
			break;
		case Cell.CELL_TYPE_FORMULA:
			o = String.valueOf(c.getCellFormula());
			break;
		case Cell.CELL_TYPE_NUMERIC:
			o = String.valueOf(c.getNumericCellValue());
			break;
		case Cell.CELL_TYPE_STRING:
			o = c.getStringCellValue();
			break;
		default:
			o = null;
			break;
		}
		return o;
	}

	private List<Object> handlerExcel2Objs(Workbook wb, Class clz, int readLine, int tailLine) {
		Sheet sheet = wb.getSheetAt(0);
		List<Object> objs = null;
		try {
			Row row = sheet.getRow(readLine);
			objs = new ArrayList<Object>();
			Map<Integer, String> maps = getHeaderMap(row, clz);
			if (maps == null || maps.size() <= 0)
				throw new RuntimeException("要读取的Excel的格式不正确，检查是否设定了合适的行");
			for (int i = readLine + 1; i <= sheet.getLastRowNum() - tailLine; i++) {
				row = sheet.getRow(i);
				Object obj = clz.newInstance();
				for (Cell c : row) {
					int ci = c.getColumnIndex();
					String mn = maps.get(ci).substring(3);
					mn = mn.substring(0, 1).toLowerCase() + mn.substring(1);
					BeanUtils.copyProperty(obj, mn, this.getCellValue(c));
				}
				objs.add(obj);
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return objs;
	}
	
//	/**
//	 * 类反射得到调用方法
//	 */
//	private List<Object[]> getGetMethod(Class<T> clazz) {
//		 // 得到所有字段   
//        Field filed[] = clazz.getDeclaredFields();   
//        // 导出的字段的get方法   
//        List<Object[]> fieldList = new ArrayList<Object[]>();   
//        // 遍历整个filed   
//        for (int i = 0; i < filed.length; i++) {   
//            Field f = filed[i];   
//            Custom exa = f.getAnnotation(Custom.class);   
//            // 如果设置了annottion   
//            if (exa != null) {   
//                String exprotName = exa.title();   
//                Integer order = exa.order();
//                // 添加到需要导出的字段的方法   
//                String fieldname = f.getName(); 
//                
//                String getMethodName = "get" 
//                        + fieldname.substring(0, 1).toUpperCase()   
//                        + fieldname.substring(1);   
//                   
//                Method getMethod;
//				try {
//					getMethod = clazz.getMethod(getMethodName,   
//					        new Class[]{});
//				} catch (Exception e) {
//					e.printStackTrace();
//					throw new BusinessException("S0000",MessageConstants.S0000);
//				}
//                Object[] fields={exprotName,order,getMethod};
//                fieldList.add(fields);   
//            }   
//        }   
//		return fieldList;
//	}
	
	/**
	 * 类反射得到调用方法
	 */
	private List<Object[]> getGetMethod(Class<T> clazz) {
		 // 得到所有字段   
        Field filed[] = clazz.getDeclaredFields();   
        // 导出的字段的get方法   
        List<Object[]> fieldList = new ArrayList<Object[]>();   
        // 遍历整个filed   
        for (int i = 0; i < filed.length; i++) {   
            Field f = filed[i];   
            Custom exa = f.getAnnotation(Custom.class);   
            // 如果设置了annottion   
            if (exa != null) {   
                String exprotName = exa.title();  
                Integer order = exa.order();
                // 添加到需要导出的字段的方法   
                String fieldname = f.getName();   
                String getMethodName = "get" 
                        + fieldname.substring(0, 1).toUpperCase()   
                        + fieldname.substring(1);   
                   
                Method getMethod;
				try {
					getMethod = clazz.getMethod(getMethodName,   
					        new Class[]{});
				} catch (Exception e) {
					e.printStackTrace();
					throw new BusinessException("S0000",MessageConstants.S0000);
				}
                Object[] fields={exprotName,order,getMethod};
                fieldList.add(fields);   
            }   
        }   
		return fieldList;
	}
	
    /**  
     * 将传入值转为字符串
     */  
    private String getStringValue(Object value) {   
        String textValue = "";   
        if (value == null)   
            return textValue;   
  
        if (value instanceof Boolean) {   
            boolean bValue = (Boolean) value;   
            textValue = "是";   
            if (!bValue) {   
                textValue = "否";   
            }   
        } else if (value instanceof Date) {   
            Date date = (Date) value;   
          //格式化时间  
            if (value instanceof Timestamp) {
            	 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); 
                 textValue = sdf.format(date);
            }else{
            	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 
                textValue = sdf.format(date);  
            }
        } else  
            textValue = value.toString();   
  
        return textValue;   
    } 

	private List<ExcelHeader> getHeaderList(Class clz) {
		List<ExcelHeader> headers = new ArrayList<ExcelHeader>();
		
		// 导出的字段的get方法   
        List<Object[]> methodList = getGetMethod(clz);  
        // 产生表格标题行    
        for (int i = 0; i < methodList.size(); i++) {  
        	String tittle = (String)methodList.get(i)[0];
        	Integer order = (Integer)methodList.get(i)[1];
        	Method ms = (Method)methodList.get(i)[2];			
			headers.add(new ExcelHeader(tittle, order, ms.getName()));
        } 
        
//		Method[] ms = clz.getDeclaredMethods();
//		for (Method m : ms) {
//			String mn = m.getName();
//			if (mn.startsWith("get")) {
//				if (m.isAnnotationPresent(Custom.class)) {
//					Custom er = m.getAnnotation(Custom.class);
//					headers.add(new ExcelHeader(er.title(), er.order(), mn));
//				}
//			}
//		}
		return headers;
	}

	private Map<Integer, String> getHeaderMap(Row titleRow, Class clz) {
		List<ExcelHeader> headers = getHeaderList(clz);
		Map<Integer, String> maps = new HashMap<Integer, String>();
		for (Cell c : titleRow) {
			String title = c.getStringCellValue();
			for (ExcelHeader eh : headers) {
				if (eh.getTitle().equals(title.trim())) {
					maps.put(c.getColumnIndex(), eh.getMethodName().replace("get", "set"));
					break;
				}
			}
		}
		return maps;
	}
}
